
#include <GL/glu.h>
#include "glDrawFunctions.h"

void glDrawModel(Mesh *model, int meshrendermode_mask) {
   
   if (meshrendermode_mask & POINTS) {
      glColor4f(0.0,0.0,0.0,1.0);
      glPushAttrib(GL_LIGHTING_BIT);
      glDisable(GL_LIGHTING);
      glPointSize(1.0);
      glDrawElements(GL_POINTS, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glPopAttrib();
   }
   else if (meshrendermode_mask & WIREFRAME) {
      glColor4f(0.0,0.0,0.0,1.0);
      glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);
      glLineWidth(1.0);
      glDisable(GL_CULL_FACE);
      glDisable(GL_LIGHTING);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glPopAttrib();
   }
   else if (meshrendermode_mask & HIDDENLINES) {
      glColor4f(0.0,0.0,0.0,1.0);
      glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);
      glLineWidth(1.0);
      glEnable(GL_CULL_FACE);
      glDisable(GL_LIGHTING);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glPopAttrib();
   }
   else if (meshrendermode_mask & FLATLINES) {
      glEnable(GL_POLYGON_OFFSET_FILL);
      glPolygonOffset(1,1);
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glDisable(GL_POLYGON_OFFSET_FILL);
      
      glColor4f(0.1,0.1,0.1,1.0);
      glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_LINE_BIT | GL_LIGHTING_BIT);
      glLineWidth(1.0);
      glEnable(GL_CULL_FACE);
      glDisable(GL_LIGHTING);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glPopAttrib();
   }
   else if (meshrendermode_mask & FLAT) {
      glEnable(GL_POLYGON_OFFSET_FILL);
      glPolygonOffset(1,1);
      glShadeModel(GL_FLAT);
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glShadeModel(GL_SMOOTH);
      glDisable(GL_POLYGON_OFFSET_FILL);
   }
   else if (meshrendermode_mask & SMOOTH) {
      glEnable(GL_POLYGON_OFFSET_FILL);
      glPolygonOffset(1,1);
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glDisable(GL_POLYGON_OFFSET_FILL);
   }
}

void glDrawModelPoints(Mesh *model) {
   
   for (uint i=0; i<model->nvertices(); ++i) {
      glPushName(i);
      glBegin(GL_POINTS);
      const Point3 &p = model->positions[i];
      glVertex3f(p[0], p[1], p[2]);
      glEnd();
      glPopName();
   }
}

void glDrawModelFaces(Mesh *model) {
   
   for (uint i=0; i<model->nfaces(); ++i) {
      glPushName(i);
      glBegin(GL_POLYGON);
      for (int j=0; j<3; ++j) {
         int id = model->faces[i*3+j];
         const Point3 &p = model->positions[id];
         const Vector3 &n = model->normals[id];
         glNormal3f(n[0], n[1], n[2]);
         glVertex3f(p[0], p[1], p[2]);
      }
      glEnd();
      glPopName();
   }
}

void glDrawModelFacesOneNormal(Mesh *model) {
   
   for (uint i=0; i<model->nfaces(); ++i) {
      const Vector3 &n = model->facenormals[i];
      glBegin(GL_POLYGON);
      glNormal3f(n[0], n[1], n[2]);
      for (int j=0; j<3; ++j) {
         int id = model->faces[i*3+j];
         const Point3 &p = model->positions[id];
         glVertex3f(p[0], p[1], p[2]);
      }
      glEnd();
   }
}

/// drawing the 3D cursor
void drawCursor3D(const Point3 &c, Number sf) {
   
   glDisable(GL_LIGHTING);
   glColor4f(1.0,0.5,0.0,1.0);
   glLineWidth(1.0);
   
   glBegin(GL_LINE_STRIP);
   Vector3 V(-1,0,0); Vector3 uV(0,1,0); /// V = crossproduct(Vector3(0,0,1),uV)
   for (float t=0; t<2.0*3.2; t+=0.1) {
      Point3 p = c + sf*cos(t)*uV + sf*sin(t)*V;
      glVertex3f(p[0], p[1], p[2]);
   }
   glEnd();
   
   Point3 p0 = c + Vector3(1,0,0)*0.5*sf;
   Point3 p1 = c + Vector3(1,0,0)*1.5*sf;
   Point3 p2 = c + Vector3(0,1,0)*0.5*sf;
   Point3 p3 = c + Vector3(0,1,0)*1.5*sf;
   Point3 p4 = c + Vector3(-1,0,0)*0.5*sf;
   Point3 p5 = c + Vector3(-1,0,0)*1.5*sf;
   Point3 p6 = c + Vector3(0,-1,0)*0.5*sf;
   Point3 p7 = c + Vector3(0,-1,0)*1.5*sf;
   
   glColor4f(1.0,0.0,0.0,1.0);
   glBegin(GL_LINES);
   glVertex3f(p0[0], p0[1], p0[2]);
   glVertex3f(p1[0], p1[1], p1[2]);
   glVertex3f(p2[0], p2[1], p2[2]);
   glVertex3f(p3[0], p3[1], p3[2]);
   glVertex3f(p4[0], p4[1], p4[2]);
   glVertex3f(p5[0], p5[1], p5[2]);
   glVertex3f(p6[0], p6[1], p6[2]);
   glVertex3f(p7[0], p7[1], p7[2]);
   glEnd();
   
   glEnable(GL_LIGHTING);
}

void glDrawPositions(const vector<Point3> &c) {
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_LIGHTING_BIT | GL_LINE_BIT | GL_POINT_BIT);
   glDisable(GL_LIGHTING);
   glEnable(GL_LINE_SMOOTH);
   glEnable(GL_POINT_SMOOTH);
   glEnable(GL_BLEND);
   
   //~ glDisable(GL_LIGHTING);
   for (unsigned i=0; i<c.size(); ++i) {
      glPushName(i);
      glBegin(GL_POINTS);
      const Point3 &p = c[i];
      glVertex3f(p[0], p[1], p[2]);
      glEnd();
      glPopName();
   }
   //~ glEnable(GL_LIGHTING);
   glPointSize(1.0);
   
   glPopAttrib();
}
